/**
 * 
 */
package br.srv.full.virtual.engine.person.auth;

import java.awt.MenuItem;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import javax.ejb.EJB;
import javax.ejb.Stateful;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import br.srv.full.virtual.bean.base.QueryID;
import br.srv.full.virtual.engine.persistence.BeansManagerLocal;
import br.srv.full.virtual.entities.auth.Role;
import br.srv.full.virtual.entities.menu.MenuBase;
import br.srv.full.virtual.entities.menu.MenuGroup;

/**
 * @author Delfino
 * 
 */
@Stateful(name = "AuthManager", description="Gerencia todo o processo de autenticação e Autorização.")
@TransactionManagement(TransactionManagementType.CONTAINER)
public class AuthManagerEJB implements AuthManagerLocal, AuthManagerRemote {

	private final Log log = LogFactory.getLog(getClass());

	@EJB
	private BeansManagerLocal beansManager;

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.srv.full.virtual.engine.auth.AuthManager#getMenu(java.lang.Class,
	 *      java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	@Override
	@TransactionAttribute(TransactionAttributeType.SUPPORTS)
	public <menu extends MenuBase> menu getMenu(Class<menu> p_menuClazz, String p_name) {
		QueryID l_queryId = QueryID.getQueryId(p_menuClazz, QueryID.FIND_BY_NAME);

		log.info("Usando o QueryId: " + l_queryId);

		menu l_menu = (menu) beansManager.find(p_menuClazz, l_queryId, p_name);
		log.info("Menu encontrado: " + l_menu);
		return l_menu;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.srv.full.virtual.engine.auth.AuthManager#getMenus(java.lang.Class,
	 *      br.srv.full.virtual.entities.base.Role[])
	 */
	@Override
	public <menu extends MenuBase> List<menu> getMenus(Class<menu> p_menuClazz, Role... p_roles) {
		StringBuilder l_ejbql = new StringBuilder("select mg from ");

		l_ejbql.append(p_menuClazz.getSimpleName());

		l_ejbql.append(" mg ");

		if (p_roles != null && p_roles.length > 0) {
			l_ejbql.append(" left join mg.roles as role where mg.roles is empty or role in (");
			for (Role l_role : p_roles) {
				l_ejbql.append("?, ");
			}
			l_ejbql.setCharAt(l_ejbql.length() - 2, ')');
		} else {
			l_ejbql.append(" where mg.roles is empty ");
		}
		l_ejbql.append(" order by mg.priority");

		ArrayList<menu> l_menus = getResult(p_menuClazz, l_ejbql, p_roles);
		log.info("Menus Encontrados: " + l_menus);
		return l_menus;
	}

	/**
	 * @param <bean>
	 * @param p_beanClazz
	 * @param l_ejbql
	 * @param p_role
	 * @return
	 */
	private <bean> ArrayList<bean> getResult(Class<bean> p_beanClazz, StringBuilder l_ejbql, Object... p_param) {
		// criar um metodo getFindResult synchorinezed no BeanManager
		Object l_find = beansManager.find(p_beanClazz, l_ejbql.toString(), p_param);

		ArrayList<bean> l_menus;
		if (l_find instanceof Collection) {
			Collection<bean> l_objs = (Collection<bean>) l_find;

			l_menus = new ArrayList<bean>(l_objs.size());
			for (bean l_menu : l_objs) {
				l_menus.add(l_menu);
			}
		} else {
			l_menus = new ArrayList<bean>(1);
			l_menus.add((bean) l_find);
		}
		log.info("Beans Encontrados: " + l_menus);
		return l_menus;
	}

	public <menu extends MenuBase> List<menu> getMenus(Class<menu> p_menuClazz, List<Role> p_roles) {
		return getMenus(p_menuClazz, p_roles.toArray(new Role[] {}));
	}

	/**
	 * find(MenuItem.class, MenuItem.FIND_PER_GROUP_AND_ROLES, p_group, p_rules)
	 * 
	 * {@link MenuItem#FIND_BY_GROUP_AND_NO_ROLES}
	 */
	public <menu extends MenuBase> List<menu> getMenus(Class<menu> p_menuClazz, MenuGroup p_group, Role... p_roles) {
		StringBuilder l_ejbql = new StringBuilder("select mg  from ");

		l_ejbql.append(p_menuClazz.getSimpleName());

		l_ejbql.append(" mg ");

		Object[] l_array = null;
		boolean l_hasRoles = p_roles != null && p_roles.length > 0;
		if (l_hasRoles) {
			l_ejbql.append(" left join mg.roles as role where (mg.roles is empty or role in (");

			for (Role l_role : p_roles) {
				l_ejbql.append("?, ");
			}
			l_ejbql.setCharAt(l_ejbql.length() - 2, ')');
			l_ejbql.append(')');
			l_array = Arrays.copyOf(p_roles, p_roles.length, Object[].class);

		} else {
			l_ejbql.append(" where mg.roles is empty ");
		}

		if (p_group != null) {
			l_ejbql.append(" and mg.group = ?");
			if (l_array == null)
				l_array = new Object[1];
			else
				l_array = Arrays.copyOf(l_array, l_array.length + 1);
			l_array[l_array.length - 1] = p_group;
		}

		l_ejbql.append(" order by mg.priority");

		ArrayList<menu> l_menus = getResult(p_menuClazz, l_ejbql, l_array);
		log.info("Menus Encontrados: " + l_menus);
		return l_menus;
	}

	public <menu extends MenuBase> List<menu> getMenus(Class<menu> p_menuClazz, MenuGroup p_group, List<Role> p_roles) {
		if (p_roles != null)
			return getMenus(p_menuClazz, p_group, p_roles.toArray(new Role[] {}));
		else
			return getMenus(p_menuClazz, p_group, new Role[] {});
	}

}
